using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace homeworkhero
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        public const int SCREENWIDTH = 1280;
        public const int SCREENHEIGHT = 720;
        public static Rectangle SAFEAREA;

        TimeSpan timeSinceLastBullet = TimeSpan.Zero;

        public const int LAYEROFFSETX = -7;
        public const int LAYERHEIGHT = 35;

        public TimeSpan TIMEBETWEENBULLETS = TimeSpan.FromMilliseconds(300.0);

        public static Enemy[] enemyTypes;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Player player1;
        Player player2 = null;
        Stage stage;

        AnimationSet projectileAnimSet;

        CollisionManager collisionManager;

        SpriteFont font;

        GamePadState prevgps = GamePad.GetState(PlayerIndex.One);
        GamePadState prevgps2 = GamePad.GetState(PlayerIndex.Two);
        KeyboardState prevkbs = Keyboard.GetState(PlayerIndex.One);

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);

            graphics.PreferredBackBufferWidth = SCREENWIDTH;
            graphics.PreferredBackBufferHeight = SCREENHEIGHT;

            collisionManager = new CollisionManager(this);

            this.IsFixedTimeStep = true;

            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            SAFEAREA = GraphicsDevice.Viewport.TitleSafeArea;

            base.Initialize();
        }

        private Texture2D[] getTexture2DArray(String prefix, int count) {
            Texture2D[] retVal = new Texture2D[count];
            for(int i=0;i<count;i++) {
                retVal[i] = this.Content.Load<Texture2D>(prefix + (i + 1));
            }
            return retVal;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            /* load stage */
            Texture2D bgImage = this.Content.Load<Texture2D>("hh bg");
            stage = new Stage(bgImage, getTexture2DArray("problems 0",8));

            /* load players */
            Animation player_a = new Animation("stand", getTexture2DArray("hero_fwd_0",8), TimeSpan.FromSeconds(0.9f), false);
            Animation player_explode = new Animation("dying", getTexture2DArray("explodey_0", 8), TimeSpan.FromSeconds(1f), true);
            AnimationSet player_aset = new AnimationSet(new Animation[] { player_a, player_explode });

            player1 = new Player(stage, player_aset,PlayerIndex.One);
            player1.setPosition(new Vector2(50.0f, 0.0f));
            stage.addSprite(player1);
            // player2 here

            /* headcar */
            Animation headcar_a = new Animation("stand", getTexture2DArray("headcar_0", 4), TimeSpan.FromSeconds(0.4f), false);
            Animation headcar_explode = player_explode;
            AnimationSet headcar_aset = new AnimationSet(new Animation[] { headcar_a, headcar_explode });
            stage.addEnemyType(new Enemy(stage, headcar_aset, Vector2.Zero, 10));

            /* bullet */
            Animation bullet_a = new Animation("stand", getTexture2DArray("bullet_0", 4), TimeSpan.FromSeconds(0.5f), false);
            projectileAnimSet = new AnimationSet(new Animation[] { bullet_a });

            font = this.Content.Load<SpriteFont>("Arial");
            /* Add Boss */
            stage.initializeBoss();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            Content.Unload();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            timeSinceLastBullet = timeSinceLastBullet.Add(gameTime.ElapsedGameTime);

            GamePadState gps = GamePad.GetState(PlayerIndex.One);
            GamePadState gps2 = GamePad.GetState(PlayerIndex.Two);
            KeyboardState kbs = Keyboard.GetState(PlayerIndex.One);

            if (gps.Buttons.Back == ButtonState.Pressed ||
                kbs.IsKeyDown(Keys.Escape))
                    this.Exit();

            if (player1.isDead() && (player2 == null || player2.isDead()))
            {
                stage.removeAllSprites();
                stage.setSpawnEnemies(false);
                return;
            }

            if(gps2.IsConnected && player2 == null) {
                player2 = new Player(stage, player1.getAnimationSet().copy(),PlayerIndex.Two);
                player2.setPosition(new Vector2(50.0f, 0.0f));
                stage.addSprite(player2);
            } else if(!gps2.IsConnected && player2 != null) {
                stage.removeSprite(player2);
                player2 = null;
            }

            if (kbs.IsKeyDown(Keys.W) || kbs.IsKeyDown(Keys.Up))
            {
                player1.setVelocityY(0.3f);
            }
            else if (kbs.IsKeyDown(Keys.S) || kbs.IsKeyDown(Keys.Down))
            {
                player1.setVelocityY(-0.3f);
            }
            else
            {
                player1.setVelocityY(0.0f);
            }

            if (kbs.IsKeyDown(Keys.D) || kbs.IsKeyDown(Keys.Right))
            {
                player1.setVelocityX(5.0f);
            }
            else if (kbs.IsKeyDown(Keys.A) || kbs.IsKeyDown(Keys.Left))
            {
                player1.setVelocityX(-5.0f);
            }
            else
            {
                player1.setVelocityX(0.0f);
            }

            if(kbs.IsKeyDown(Keys.F1) && prevkbs.IsKeyUp(Keys.F1)) {
                player1.damage(-100);
                if(!stage.getPlayers().Contains(player1)) {
                    stage.addSprite(player1);
                }
            }

            //shoots
            if (kbs.IsKeyDown(Keys.Space) && timeSinceLastBullet.CompareTo(TIMEBETWEENBULLETS) > 0)
            {
                shootProjectile(player1);
            }
            
            if(gps.IsConnected) {
                if(gps.ThumbSticks.Left.X != 0 || gps.ThumbSticks.Left.Y != 0) {
                    player1.setVelocityX(gps.ThumbSticks.Left.X * 5.0f);
                    player1.setVelocityY(gps.ThumbSticks.Left.Y * 0.3f);
                }

                if (gps.Buttons.A == ButtonState.Pressed && timeSinceLastBullet.CompareTo(TIMEBETWEENBULLETS) > 0)
                {
                    shootProjectile(player1);
                }
            }
            if(gps2.IsConnected && player2 != null) {
                player2.setVelocityX(gps2.ThumbSticks.Left.X * 5.0f);
                player2.setVelocityY(gps2.ThumbSticks.Left.Y * 0.3f);

                if (gps2.Buttons.A == ButtonState.Pressed && timeSinceLastBullet.CompareTo(TIMEBETWEENBULLETS) > 0)

                {
                    shootProjectile(player2);
                }
            }
            
            // update logic
            stage.update(gameTime);
            collisionManager.checkCollisions(stage.getPlayers(), stage.getSprites());

            base.Update(gameTime);

            prevkbs = kbs;
            prevgps = gps;
            prevgps2 = gps2;
        }

        private void shootProjectile(Player player) {
            Projectile nProjectile = new Projectile(stage,
                new Vector2(player.getPosition().X, player.getPosition().Y),
                new Vector2(player.getVelocity().X+10, 0), projectileAnimSet.copy());
            stage.addSprite(nProjectile);
            timeSinceLastBullet = TimeSpan.Zero;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                SpriteSortMode.BackToFront, SaveStateMode.SaveState);
            stage.draw(spriteBatch);
            spriteBatch.End();

            spriteBatch.Begin();
            if (player1.isDead() && (player2 == null || player2.isDead()))
            {
                spriteBatch.DrawString(font, "You died.", new Vector2(SAFEAREA.Center.X, SAFEAREA.Center.Y), Color.Red);
            } else {
                spriteBatch.DrawString(font, "Health: " + player1.getHealth().ToString() 
                    + "  Distance: " + ((int)player1.getPosition().X / 10).ToString(), 
                    new Vector2(SAFEAREA.Left + 10, SAFEAREA.Top + 10), 
                    Color.Red);
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
